package jwlee.testldap;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

/**
 * <p>Title:</p>
 * <p>Description:</p>
 * <p>Copyright: Copyright (c) 2008 Nanjing Xian Zhi Lu</p>
 * <p>Company:Nanjing Thinking Road Technology Co.,Ltd.</p>
 * @author hubert
 * <p>Datetime 2008-8-26 下午02:27:08</p>
 */

/* -------------------------修订记录--------------------------------
 * 1.
 *    修改原因：
 *    修改内容：
 *    修改时间：
 *    修改人员：
 * ---------------------------------------------------------------
 */

public class LDAPBaseOperation {
    private static final String DEFAULT_LDAP_CTX_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
    private static final SearchControls DEFAULT_SEARCH_CONS = new SearchControls(SearchControls.SUBTREE_SCOPE, 10000, 10000, null, true, true);

    private String providerURL;

    private String user;
    private String password;

    private DirContext context;

    private Hashtable<String, Object> env = new Hashtable<String, Object>();

    /**
     * 设置要链接的LDAP数据库地址
     * @param providerURL The providerURL to set.
     */
    public void setProviderURL(String providerURL) {
        this.providerURL = providerURL;
    }

    /**
     * 用户名
     * @param user The user to set.
     */
    public void setUser(String user) {
        this.user = user;
    }

    /**
     * 密码
     * @param password The password to set.
     */
    public void setPassword(String password) {
        this.password = password;
    }

    public static LDAPBaseOperation getInstance(String url, String user, String password) {
        LDAPBaseOperation operation = new LDAPBaseOperation();
        operation.setProviderURL(url);
        operation.setUser(user);
        operation.setPassword(password);
        return operation;
    }

    /**
     * 外部应用需要直接操作DirContext的时候，通过此方法获取Context
     * @return Returns the context.
     */
    public DirContext getContext() {
        return this.context;
    }



    /**
     * 打开与LDAP的连接
     * @return
     */
    public void open() throws NamingException{
        this.env.clear();
        this.env.put(Context.INITIAL_CONTEXT_FACTORY, DEFAULT_LDAP_CTX_FACTORY);
        this.env.put(Context.PROVIDER_URL, this.providerURL);
        this.env.put(Context.SECURITY_PRINCIPAL, this.user);
        this.env.put(Context.SECURITY_CREDENTIALS, this.password);
        this.context = new InitialDirContext(this.env);
    }

    public void close() {
        try {
            this.context.close();
        } catch (NamingException e) {
            e.printStackTrace();
        } finally {
            try {this.context.close();} catch (NamingException e) {}
        }
    }

    /**
     * 获取指定对象的全部属性
     * @param name
     * @return
     */
    public Attributes get(String name) {
        try {
            return this.context.getAttributes(name);
        } catch (NamingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取指定对象的指定属性
     * @param name
     * @param attrIds
     * @return
     */
    public Attributes get(String name, String[] attrIds) {
        try {
            return this.context.getAttributes(name, attrIds);
        } catch (NamingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据查询条件查询
     * @param name
     * @param filter
     * @param cons
     * @return
     */
    public NamingEnumeration<SearchResult> search(String name, String filter) {
        try {
            return this.context.search(name, filter, DEFAULT_SEARCH_CONS);
        } catch (NamingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 创建一个节点
     * @param name 节点路径（含其本身）
     * @param attrs 节点属性
     * @return
     * @throws NamingException
     */
    public DirContext create(String name, Attributes attrs) throws NamingException {
        return this.context.createSubcontext(name, attrs);
    }

    /**
     * 节点更名（更改路径）
     * @param oldName 原有路径
     * @param newName 新路径
     * @throws NamingException
     */
    public void rename(String oldName, String newName) throws NamingException{
        this.context.rename(oldName, newName);
    }

    /**
     * 修改节点属性
     * @param name
     * @param mod_op 指定节点的操作类型，参考javax.naming.directory.DirContext类
     * @param attrs
     * @throws NamingException
     */
    public void modify(String name, int mod_op,  Attributes attrs) throws NamingException {
        this.context.modifyAttributes(name, mod_op, attrs);
    }

    /**
     * 移除节点
     * @param name
     * @throws NamingException
     */
    public void remove(String name) throws NamingException {
        this.context.destroySubcontext(name);
    }

    public static void main(String[] args) {
        LDAPBaseOperation lo = LDAPBaseOperation.getInstance("ldap://localhost:389/dc=hubert,dc=com", "cn=Manager,dc=hubert,dc=com", "hubert");
        try {
            lo.open();
        } catch (NamingException e) {
            e.printStackTrace();
        }

        //获取全部属性
        System.out.println("cn=王瑞,ou=贤之路,ou=公司,o=addressbook,dc=hubert,dc=com 的全部属性");
        System.out.println("\t" + lo.get("cn=王瑞,ou=贤之路,ou=公司,o=addressbook"));
        System.out.println("\n\n");

        //获取指定属性
        System.out.println("cn=王瑞,ou=贤之路,ou=公司,o=addressbook,dc=hubert,dc=com 的指定属性cn");
        System.out.println(lo.get("cn=王瑞,ou=贤之路,ou=公司,o=addressbook", new String[] {"cn"}));
        System.out.println("\n\n");

        //查询给定条件的对象
        System.out.println("列出 ou=贤之路,ou=公司,o=addressbook,dc=hubert,dc=com 的下属对象，并且查询条件是：objectClass为inetOrgPerson并且具有mobile属性值");
        NamingEnumeration<SearchResult> result = lo.search("ou=贤之路,ou=公司,o=addressbook", "(&(objectClass=inetOrgPerson)(mobile=*))");


        try {
            while (result.hasMore()) {
                SearchResult sr = (SearchResult)result.next();
                System.out.println("\t>>>" + sr.getName() + "\t->" + sr.getAttributes());
            }
        } catch (NamingException e) {
            e.printStackTrace();
        }

        System.out.println("\n\n");

        try {
            //新增一个节点
            Attributes attrs = new BasicAttributes(true);

            Attribute objclass = new BasicAttribute("objectclass");
            objclass.add("top");
            objclass.add("inetOrgPerson");
            attrs.put(objclass);

            Attribute cn = new BasicAttribute("cn");
            cn.add("测试节点");
            attrs.put(cn);

            Attribute sn = new BasicAttribute("sn");
            sn.add("测试节点");
            attrs.put(sn);

            lo.create("cn=测试节点,ou=贤之路,ou=公司,o=addressbook", attrs);

            System.out.println("新增的节点为：\t" + lo.get("cn=测试节点,ou=贤之路,ou=公司,o=addressbook"));
            System.out.println("\n\n");

            //修改节点的属性
            Attributes attrsM = new BasicAttributes(true);
            Attribute mobile = new BasicAttribute("mobile");
            mobile.add("13234234234");
            attrsM.put(mobile);

            lo.modify("cn=测试节点,ou=贤之路,ou=公司,o=addressbook", DirContext.ADD_ATTRIBUTE, attrsM);

            System.out.println("变更后的节点为：\t" + lo.get("cn=测试节点,ou=贤之路,ou=公司,o=addressbook"));
            System.out.println("\n\n");

            //更改节点
            lo.rename("cn=测试节点,ou=贤之路,ou=公司,o=addressbook", "cn=测试,ou=贤之路,ou=公司,o=addressbook");
            System.out.println("查找被更名后的节点为：\t" + lo.get("cn=测试,ou=贤之路,ou=公司,o=addressbook"));
            System.out.println("\n\n");

            //删除节点
            try {
                lo.remove("cn=测试,ou=贤之路,ou=公司,o=addressbook");

                System.out.println("删除成功！");

                Attributes a = lo.get("cn=测试,ou=贤之路,ou=公司,o=addressbook");

                System.out.println("查找被删除的节点为(如果未找到对象之前的查找过程应该会打印出异常跟踪)：\t" + a);

            } catch (Exception e) {
                System.out.println("将要被删除的节点不存在！");
            }
            System.out.println("\n\n");

        } catch (NamingException e) {
            e.printStackTrace();
        }

        lo.close();
    }
}
